package ip_parser

import (
	"errors"
	"fmt"
	"log"
	"math"
	"net"
	"regexp"
	"strconv"
	"strings"
)

type IPParser struct {
	ipStr       string
	excludeStr  string
	ipRangeList []iPRange
	length      int
}

func NewIPParser(ipStr, excludeStr string) (*IPParser, error) {
	p := &IPParser{ipStr: ipStr, excludeStr: excludeStr}
	p.length = -1
	result, err := p.getIPRangeListWithExcludeStr(p.ipStr, p.excludeStr)
	if err != nil {
		return p, nil
	}
	p.ipRangeList = result
	return p, nil
}

func (p IPParser) Len() int {
	if p.length < 0 {
		p.length = 0
		for _, ipRange := range p.ipRangeList {
			p.length += ipRange.len()
			log.Println(ipRange.string())
		}
	}
	return p.length
}

func (p IPParser) Range(rangeFunc func(ip string) bool) {
	for _, ipRange := range p.ipRangeList {
		err := ipRange.rangeIP(rangeFunc)
		if err != nil {
			if err.Error() == "stop range" {
				break
			} else {
				log.Panic(err)
			}
		}
	}
}

func (p IPParser) getIPRangeList(ipStr string) ([]iPRange, error) {
	ipStr = strings.ReplaceAll(ipStr, "\n", ",")
	ipStr = strings.ReplaceAll(ipStr, " ", "")
	newAddr := strings.Split(ipStr, ",")
	if ipStr == "" {
		return nil, fmt.Errorf(`格式错误: ipStr为空`)
	}
	var ipRangeList []iPRange
	for _, ip := range newAddr {
		r := iPRange{}
		err := r.parse(ip)
		if err != nil {
			return nil, err
		}
		ipRangeList = append(ipRangeList, r)
	}
	return ipRangeList, nil
}

func (p IPParser) getIPRangeListWithExcludeStr(ipStr, excludeStr string) ([]iPRange, error) {
	ipRangeList, err := p.getIPRangeList(ipStr)
	if err != nil {
		return nil, err
	}
	var excIPRangeList []iPRange
	excIPRangeList, err = p.getIPRangeList(excludeStr)
	if err != nil && err.Error() != "格式错误: ipStr为空" {
		return nil, err
	}

	ipRangeList = merge(ipRangeList)
	excIPRangeList = merge(excIPRangeList)

	var result []iPRange
	if len(excIPRangeList) > 0 {
		result = remove(ipRangeList, excIPRangeList)
	} else {
		result = ipRangeList
	}
	// for _, ipRange := range result {
	// 	log.Println(ipRange.string())
	// }
	return result, nil
}

// iPRange ip 范围: [start, end)
type iPRange struct {
	start int
	end   int
}

func (r *iPRange) parse(ipStr string) error {
	if strings.Contains(ipStr, "-") {
		ips := strings.Split(ipStr, "-")
		if len(ips) != 2 {
			return fmt.Errorf("格式错误: %s", ipStr)
		}
		start, err := IPString2Long(ips[0])
		if err != nil {
			return fmt.Errorf("格式错误: %s, %s", ipStr, err.Error())
		}
		r.start = start
		end, err := IPString2Long(ips[1])
		if err != nil {
			return fmt.Errorf("格式错误: %s, %s", ipStr, err.Error())
		}
		r.end = end + 1
		if r.end <= r.start {
			return fmt.Errorf("IP范围错误: %s", ipStr)
		}
	} else if strings.Contains(ipStr, "/") {
		ip, _, err := net.ParseCIDR(ipStr)
		if err != nil {
			return err
		}
		start, err := IPString2Long(ip.String())
		if err != nil {
			return err
		}
		r.start = start

		i := strings.Index(ipStr, "/")
		mask := ipStr[i+1:]
		n, err := strconv.Atoi(mask)
		if err != nil {
			return err
		}
		r.end = r.start + 1<<(32-n)
	} else {
		start, err := IPString2Long(ipStr)
		if err != nil {
			return fmt.Errorf("格式错误: %s, %s", ipStr, err.Error())
		}
		r.start = start
		r.end = r.start + 1
	}
	return nil
}

func (r iPRange) len() int {
	length := r.end - r.start
	if length < 0 {
		return 0
	}
	return length
}

func (r iPRange) rangeIP(rangeFunc func(string) bool) error {
	var ip string
	var err error
	for start := r.start; start < r.end; start++ {
		ip, err = Long2IPString(start)
		if err != nil {
			return err
		}
		if !rangeFunc(ip) {
			return errors.New("stop range")
		}
	}
	return nil
}

func (r iPRange) sub(dst iPRange) []iPRange {
	if !r.intersect(dst) {
		return []iPRange{r}
	}

	r1 := iPRange{start: r.start, end: dst.start}
	r2 := iPRange{start: dst.end, end: r.end}

	if r1.len() == 0 {
		if r2.len() == 0 {
			return nil
		} else {
			return []iPRange{r2}
		}
	} else {
		if r2.len() == 0 {
			return []iPRange{r1}
		} else {
			return []iPRange{r1, r2}
		}
	}
}

func (r iPRange) add(dst iPRange) []iPRange {
	if !r.intersect(dst) {
		return []iPRange{r, dst}
	}
	var start, end = r.start, r.end
	if dst.start < start {
		start = dst.start
	}
	if dst.end > end {
		end = dst.end
	}
	return []iPRange{{start: start, end: end}}
}

// 是否有交集
func (r iPRange) intersect(dst iPRange) bool {
	if r.start >= dst.end || dst.start >= r.end {
		return false
	}
	return true
}

func (r iPRange) string() string {
	start, err := Long2IPString(r.start)
	if err != nil {
		return fmt.Sprintf("Error: %s", err.Error())
	}
	end, err := Long2IPString(r.end - 1)
	if err != nil {
		return fmt.Sprintf("Error: %s", err.Error())
	}
	return fmt.Sprintf("%s-%s", start, end)
}

// 合并多个集合
func merge(ipRangeList []iPRange) []iPRange {
	var tmpList []iPRange
	var size = len(ipRangeList)
	var intersectIndex = -1
	var srcIndex = 0
	for ; srcIndex < size; srcIndex++ {
		for i := 0; i < size; i++ {
			if i == srcIndex {
				continue
			}
			if ipRangeList[srcIndex].intersect(ipRangeList[i]) {
				intersectIndex = i
				break
			}
		}
		if intersectIndex >= 0 {
			break
		}
	}

	if intersectIndex >= 0 {
		for i := 0; i < size; i++ {
			if i == srcIndex {
				continue
			}
			if i == intersectIndex {
				tmpList = append(tmpList, ipRangeList[srcIndex].add(ipRangeList[intersectIndex])[0])
			} else {
				tmpList = append(tmpList, ipRangeList[i])
			}
		}
		return merge(tmpList)
	} else {
		return ipRangeList
	}
}

// 缩减集合
func remove(src []iPRange, dst []iPRange) []iPRange {
	var tmpSrc []iPRange
	var srcIndex, dstIndex int
	hasIntersect := false
	for i := range src {
		for j := range dst {
			if src[i].intersect(dst[j]) {
				hasIntersect = true
				srcIndex = i
				dstIndex = j
				break
			}
		}
	}
	if hasIntersect {
		tmpSrc = append(tmpSrc, src[srcIndex].sub(dst[dstIndex])...)
		for i := range src {
			if i == srcIndex {
				continue
			}
			tmpSrc = append(tmpSrc, src[i])
		}
		return remove(tmpSrc, dst)
	} else {
		return src
	}
}

func inc(ip net.IP) {
	for j := len(ip) - 1; j >= 0; j-- {
		ip[j]++
		if ip[j] > 0 {
			break
		}
	}
}

func IPString2Long(ip string) (int, error) {
	b := net.ParseIP(ip).To4()
	if b == nil {
		return 0, errors.New("invalid ipv4 format")
	}

	return int(b[3]) | int(b[2])<<8 | int(b[1])<<16 | int(b[0])<<24, nil
}

func Long2IPString(i int) (string, error) {
	if i > math.MaxUint32 {
		return "", errors.New("beyond the scope of ipv4")
	}

	ip := make(net.IP, net.IPv4len)
	ip[0] = byte(i >> 24)
	ip[1] = byte(i >> 16)
	ip[2] = byte(i >> 8)
	ip[3] = byte(i)

	return ip.String(), nil
}

func IsIPv4(addr string) bool {
	ipReg := `^((0|[1-9]\d?|1\d\d|2[0-4]\d|25[0-5])\.){3}(0|[1-9]\d?|1\d\d|2[0-4]\d|25[0-5])$`
	match, _ := regexp.MatchString(ipReg, addr)
	if match {
		return true
	}
	return false
}

func GetOutboundIP() (string, error) {
	conn, err := net.Dial("udp", "114.114.114.114:80")
	if err != nil {
		return "", err
	}
	defer conn.Close()
	localAddr := conn.LocalAddr().(*net.UDPAddr)
	return localAddr.IP.String(), nil
}

func GetLocalIP() (string, error) {
	ip, err := GetOutboundIP()
	if err != nil {
		addrList, err := net.InterfaceAddrs()
		if err != nil {
			return "", err
		}
		for _, address := range addrList {
			// 检查ip地址判断是否回环地址
			if ipAddr, ok := address.(*net.IPNet); ok && !ipAddr.IP.IsLoopback() {
				if ipAddr.IP.To4() != nil {
					return ipAddr.IP.String(), err
				}
			}
		}
	}
	return ip, errors.New("没有找到IP地址")
}

func GetIPByInterfaceName(interfaceName string) (string, error) {
	var i *net.Interface
	var err error
	if i, err = net.InterfaceByName(interfaceName); err != nil { // get interface
		return "", err
	}
	if addrs, err := i.Addrs(); err == nil {
		for _, addr := range addrs {
			if ipAddr, ok := addr.(*net.IPNet); ok && ipAddr.IP.To4() != nil {
				return ipAddr.IP.To4().String(), nil
			}
		}
	}
	return "", err
}
